using System;
using System.Collections.Generic;
using System.Text;
using NBusiness.CodeDom;
using NBusiness.ESharp.Compiler;
using NBusiness.CodeDom.Compiler;

namespace NBusiness.ESharp
{
    [Serializable]
    public class ESharpField : EntityField
    {
        private static Dictionary<string, Type> _fieldTypes = null;

        #region Get Field Types
        public static Type GetFieldType(string fieldType)
        {
            return GetFieldType(fieldType, false);
        }

        public static string[] GetFieldTypeNames()
        {
            List<string> names = new List<string>();
            foreach (string name in _fieldTypes.Keys)
            {
                if (!name.EndsWith("?")) names.Add(name);
            }
            return names.ToArray();
        }

        public static Type GetFieldType(string fieldType, bool isNullable)
        {
            if (fieldType == null)
                throw new ArgumentNullException("fieldType");

            //Lazy initialization
            if (_fieldTypes == null)
            {
                _fieldTypes = new Dictionary<string, Type>();
                _fieldTypes.Add("int", typeof(int));
                _fieldTypes.Add("int?", typeof(int?));
                _fieldTypes.Add("string", typeof(string));
                _fieldTypes.Add("string?", typeof(string));
                _fieldTypes.Add("unique", typeof(Guid));
                _fieldTypes.Add("unique?", typeof(Guid?));
                _fieldTypes.Add("bytes", typeof(byte[]));
                _fieldTypes.Add("bytes?", typeof(byte[]));
                _fieldTypes.Add("short", typeof(short));
                _fieldTypes.Add("short?", typeof(short?));
                _fieldTypes.Add("long", typeof(long));
                _fieldTypes.Add("long?", typeof(long?));
                _fieldTypes.Add("decimal", typeof(decimal));
                _fieldTypes.Add("decimal?", typeof(decimal?));
                _fieldTypes.Add("double", typeof(double));
                _fieldTypes.Add("double?", typeof(double?));
                _fieldTypes.Add("float", typeof(float));
                _fieldTypes.Add("float?", typeof(float?));
                _fieldTypes.Add("datetime", typeof(DateTime));
                _fieldTypes.Add("datetime?", typeof(DateTime?));
                _fieldTypes.Add("bool", typeof(bool));
                _fieldTypes.Add("bool?", typeof(bool?));
            }

            fieldType = (isNullable && !fieldType.EndsWith("?") ? fieldType + "?" : fieldType.TrimEnd('?'));

            Type t = null;
            if (_fieldTypes.ContainsKey(fieldType))
                t = _fieldTypes[fieldType];

            return t;
        }
        #endregion

        protected override void Attach()
        {
            //Nothing
        }

        protected override void InitializeAssignmentLogic()
        {
            //Assign auto
            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.FieldAutoProperty,
                new AssignmentLogicHandler(AssignAuto) ));

            //Assign id
            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.FieldIdProperty,
                new AssignmentLogicHandler(AssignId) ));

            //Assign nullable
            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.FieldNullableProperty,
                new AssignmentLogicHandler(AssignNullable) ));

            // Assign readonly
            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.FieldReadOnly,
                new AssignmentLogicHandler(AssignReadOnly) ));

            //Assign type
            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.FieldType,
                new AssignmentLogicHandler(AssignType) ));

            //Assign name
            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.FieldName,
                new AssignmentLogicHandler(AssignName) ));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.StatementEnd,
                PostAssignmentAction.Stop ));

            Assignments.Add(new AssignmentLogic( ESharpEntityElementTypes.WhiteSpace ));
            Assignments.Add(new AssignmentLogic( ESharpEntityElementTypes.Comment ));
        }

        private void AssignName(object sender, AssignmentLogicEventArgs e)
        {
            Name = e.Elements.Peek().Value;
        }

        private void AssignType(object sender, AssignmentLogicEventArgs e)
        {
            Type = ESharpField.GetFieldType(e.Elements.Peek().Value, IsNullable);

            if (Type == null)
                OnBuildError(new ESharpBuildError(ESharpErrorCode.ES2000, e.Elements.Peek()));
        }

        private void AssignReadOnly(object sender, AssignmentLogicEventArgs e)
        {
            IsReadOnly = true;
        }

        private void AssignNullable(object sender, AssignmentLogicEventArgs e)
        {
            IsNullable = true;
        }

        private void AssignAuto(object sender, AssignmentLogicEventArgs e)
        {
            IsAuto = true;
        }

        private void AssignId(object sender, AssignmentLogicEventArgs e)
        {
            IsId = true;
            if (IsAuto) IsReadOnly = true;
        }
    }
}
